package com.yf.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class NioTcpServer {
    private static final int PORT = 8888;

    private static final int BUFFER_SIZE = 1024;

    public static void main(String[] args) {
        // 1. 初始化服务器核心组件
        try (
            // 创建 Selector（多路复用器），try-with-resources 自动关闭
            Selector selector = Selector.open();
            // 创建 ServerSocketChannel（服务端通道）
            ServerSocketChannel serverChannel = ServerSocketChannel.open()
        ) {
            // 2. 配置服务端通道
            serverChannel.configureBlocking(false); // 设置为非阻塞模式
            serverChannel.bind(new InetSocketAddress(PORT)); // 绑定端口

            // 3. 向 Selector 注册 ACCEPT 事件（监听客户端连接）
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("NIO TCP 服务器启动成功，监听端口：" + PORT);
            System.out.println("等待客户端连接...");

            // 4. 循环监听事件
            while (true) {
                // 阻塞等待事件触发（返回触发的事件数）
                int readyChannels = selector.select();
                if (readyChannels == 0) {
                    continue; // 无事件触发，继续等待
                }

                // 5. 遍历触发的事件（SelectionKey 对应通道和事件的绑定）
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();

                    // 6. 处理 ACCEPT 事件（客户端连接请求）
                    if (key.isAcceptable()) {
                        handleAccept(key, selector);
                    }

                    // 7. 处理 READ 事件（客户端发送数据）
                    if (key.isReadable()) {
                        handleRead(key);
                    }

                    // 8. 移除已处理的事件
                    keyIterator.remove();
                }
            }

        } catch (IOException e) {
            System.err.println("服务器异常：" + e.getMessage());
            e.printStackTrace();
        }
    }


    private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
        // 从 SelectionKey 中获取服务端通道
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        // 接受客户端连接（非阻塞，因为通道已配置为非阻塞）
        SocketChannel clientChannel = serverChannel.accept();

        if (clientChannel != null) { // 可能返回 null（非阻塞模式下无连接时）
            System.out.println("新客户端连接：" + clientChannel.getRemoteAddress());
            clientChannel.configureBlocking(false); // 客户端通道也设置为非阻塞
            // 向 Selector 注册 READ 事件（监听客户端发送数据）
            clientChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(BUFFER_SIZE));
        }
    }


    private static void handleRead(SelectionKey key) throws IOException {
        // 从 SelectionKey 中获取客户端通道和绑定的缓冲区
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();

        // 1. 从内核缓冲区读取客户端数据到缓冲区
        int bytesRead = clientChannel.read(buffer);
        if (bytesRead == -1) {
            // 客户端关闭连接（read 返回 -1 表示连接断开）
            System.out.println("客户端断开连接：" + clientChannel.getRemoteAddress());
            clientChannel.close();
            key.cancel(); // 取消 Selector 对该通道的监听
            return;
        }

        // 2. 切换缓冲区为「读模式」
        buffer.flip();
        // 3. 从缓冲区读取数据并转换为字符串
        String receivedData = StandardCharsets.UTF_8.decode(buffer).toString().trim();
        System.out.printf("收到客户端 %s 的数据：%s%n", clientChannel.getRemoteAddress(), receivedData);

        // 4. 向客户端响应数据（回声服务：原样返回+提示）
        String response = "服务器已收到：" + receivedData + "\n";
        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));
        clientChannel.write(responseBuffer);

        // 5. 清空缓冲区（准备下次读取）
        buffer.clear();
    }
}
